package com.thysdy.flowable.service;

import com.google.gson.Gson;
import com.thysdy.flowable.common.PersonInfoUtil;
import com.thysdy.flowable.entity.*;
import com.thysdy.flowable.enums.CodeEnum;
import com.thysdy.flowable.exception.MyException;
import com.thysdy.flowable.flowable.BaseProcessService;
import com.thysdy.flowable.flowable.FlowProcessDiagramGenerator;
import com.thysdy.flowable.flowable.FlowableBpmnModelServiceImpl;
import com.thysdy.flowable.mapper.TaskInfoMapper;
import com.thysdy.flowable.service.impl.FlowablePersonInfoService;
import com.thysdy.flowable.util.TransferUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
public class ApplicationService extends BaseProcessService {
    @Autowired
    CommonService commonService;
    @Autowired
    TaskInfoMapper taskInfoMapper;
    @Autowired
    FlowableBpmnModelServiceImpl flowableBpmnModelService;
    @Autowired
    FlowProcessDiagramGenerator flowProcessDiagramGenerator;
    @Autowired
    TaskService myTaskService;
    @Autowired
    IProcessassignConfigService processassignConfigService;
    @Autowired
    FlowablePersonInfoService personInfoService;

    public void createImage(String processInstanceId, HttpServletResponse response) throws MyException {
        try {
            //1.获取当前的流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            String processDefinitionId = null;
            List<String> activeActivityIds = new ArrayList<>();
            List<String> highLightedFlows = new ArrayList<>();
            //2.获取所有的历史轨迹线对象
            List<HistoricActivityInstance> historicSquenceFlows = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW).list();
            historicSquenceFlows.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
            //3. 获取流程定义id和高亮的节点id
            if (processInstance != null) {
                //3.1. 正在运行的流程实例
                processDefinitionId = processInstance.getProcessDefinitionId();
                activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
            } else {
                //3.2. 已经结束的流程实例
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
                if (null == historicProcessInstance) {
                    return;
                }
                processDefinitionId = historicProcessInstance.getProcessDefinitionId();
                //3.3. 获取结束节点列表
                List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
                List<String> finalActiveActivityIds = activeActivityIds;

                historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
            }
            InputStream inputStream = null;
            OutputStream out = null;
            try {
                //4. 获取bpmnModel对象
                BpmnModel bpmnModel = flowableBpmnModelService.getBpmnModelByProcessDefId(processDefinitionId);
                //5. 生成图片流
                inputStream = flowProcessDiagramGenerator.generateDiagram(bpmnModel, activeActivityIds, highLightedFlows);
                out = null;
                byte[] buf = new byte[1024];
                int legth = 0;

                out = response.getOutputStream();
                while ((legth = inputStream.read(buf)) != -1) {
                    out.write(buf, 0, legth);
                }
            } catch (IOException e) {
                e.printStackTrace();
                throw new MyException(CodeEnum.commonException);
            } finally {
                IOUtils.closeQuietly(out);
                IOUtils.closeQuietly(inputStream);
            }

        } catch (Exception e) {
            log.error("create image error!", e);
            throw new MyException(CodeEnum.commonException);
        }
    }

    public List<CommentVo> getCommentsByInstanceId(String id) throws MyException {
        List<CommentVo> list = new ArrayList<>();
        try {
            list = taskInfoMapper.getCommentsByInstanceId(id);
            for (CommentVo comment : list) {
                comment.setTypeName(CommentTypeEnum.getEnumMsgByType(comment.getType()));
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new MyException(CodeEnum.commonException);
        }
        return list;
    }

    public String startProcessInstance(StartProcessInstance param) throws MyException {
        ProcessInstance processInstance = null;
        String userNameString = null;
        List<String> users = new ArrayList<>();
        try {
            String id = param.getBusinessKey();
            //设置创建者id
            identityService.setAuthenticatedUserId(param.getCreator());
            Map<String, Object> variables = param.getVariables();
            if (null == variables) {
                variables = new HashMap<>();
            }
            variables.put("latestUserId", param.getCreator());
            List<String> assignUsers = param.getAssignUsers();
            List<String> assignGroups = param.getAssignGroups();
            if (null != assignUsers && !assignUsers.isEmpty()) {
                variables.put("assignUsers", assignUsers);
                users.addAll(assignUsers);
            }
            if (null != assignGroups && !assignGroups.isEmpty()) {
                variables.put("assignGroups", assignGroups);
            }

            Map<String, Map<String, List<String>>> globalAssignSettings = (Map<String, Map<String, List<String>>>) variables.get("globalAssignSettings");

            if (null == globalAssignSettings) {
                globalAssignSettings = param.getGlobalAssignSettings();
                if (null != globalAssignSettings && !globalAssignSettings.isEmpty()) {
                    variables.put("globalAssignSettings", globalAssignSettings);
                }
            }
            if (null != globalAssignSettings && !globalAssignSettings.isEmpty()) {
                //更新配置模板
                ProcessassignConfig config = new ProcessassignConfig();
                config.setDefinitionKey(param.getProcessDefinitionKey());
                config.setConfigJson(new Gson().toJson(globalAssignSettings));
                processassignConfigService.saveOrUpdate(config);
            }

            //是否跳过第一个任务节点
            Boolean isSkipFirstTask = param.getIsSkipFirstTask();
            if (null != isSkipFirstTask && isSkipFirstTask) {
                variables.put("skipFirstTask", true);
            }
            //发起流程
            processInstance = runtimeService.createProcessInstanceBuilder()
                    .businessKey(id)
                    .variables(variables)
                    .processDefinitionKey(param.getProcessDefinitionKey())
                    .start();

            Task nextTask = null;

            //是否跳过第一个任务节点
            if (null != isSkipFirstTask && isSkipFirstTask) {
                if (null == nextTask) {
                    List<Task> tasks =taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
                    nextTask = tasks.get(0);
                }
                Map<String, Object> localVariable = new HashMap<>();
                localVariable.put("agree", true);
                RequestVo requestVo = new RequestVo();
                requestVo.setIds(Arrays.asList(nextTask.getId()));
                requestVo.setLocalVariables(localVariable);
                requestVo.setUserCode(param.getCreator());
                myTaskService.doTask(requestVo);
            }

            if (users.isEmpty() && null != globalAssignSettings && !globalAssignSettings.isEmpty()) {
                //全局配置查询审核人
                nextTask = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
                Map<String, List<String>> nextTaskSetting = globalAssignSettings.get(nextTask.getTaskDefinitionKey());
                if (null != nextTaskSetting) {
                    List<String> nextTaskUsers = nextTaskSetting.get("assignUsers");
                    if (null != nextTaskUsers && !nextTaskUsers.isEmpty()) {
                        users.addAll(nextTaskUsers);
                    }
                }
            }
            //查询派发人
            if (!users.isEmpty()) {
                Map<String, ActIdUser> userList = personInfoService.getPersonInfoList(users);
                if (null != userList) {
                    userNameString = PersonInfoUtil.getUserNameString(userList);
                }

            }
            //添加审批记录
            String message = null;
            if (null == param.getMessage()) {
                message = "发起审核申请";
            } else {
                message = param.getMessage();
            }
            if (StringUtils.isNotBlank(userNameString)) {
                message = message + "\n任务派发至: " + userNameString;
            }
            this.addComment(param.getCreator(), processInstance.getProcessInstanceId(),
                    CommentTypeEnum.TJ.toString(), message);

            //设置下一任务审核人
            commonService.addAssignUserOrGroup(assignUsers, assignGroups, globalAssignSettings, processInstance.getProcessInstanceId(), nextTask);
        } catch (Exception e) {
            log.error("start processInstance error!", e);
            throw new MyException(CodeEnum.commonException);
        }
        return processInstance.getProcessInstanceId();
    }

    public Map<String, String>  batchStartProcessInstance(StartProcessInstance param) throws MyException {
        try {
            Map<String, Map<String, Object>> businessKeyAndVariables = param.getBusinessKeyAndVariables();
            Map<String, String> resultMap = new HashMap<>();
            if (null != businessKeyAndVariables && !businessKeyAndVariables.isEmpty()) {
                for (String key : businessKeyAndVariables.keySet()) {
                    Map<String, Object> variables = businessKeyAndVariables.get(key);
                    StartProcessInstance instance = TransferUtil.transfer(param, StartProcessInstance.class);
                    Object processDefinitionKey = variables.get("processDefinitionKey");
                    if (null != processDefinitionKey) {
                        instance.setProcessDefinitionKey((String) processDefinitionKey);
                    }
                    instance.setBusinessKey(key);
                    instance.setVariables(variables);
                    String id = startProcessInstance(instance);
                    if (null != id) {
                        resultMap.put(key, id);
                    }
                }
            }
            return resultMap;
        } catch (MyException e) {
            log.error("batch start ProcessInstance error!", e);
            e.printStackTrace();
            throw new MyException(CodeEnum.commonException);
        }
    }

    public List<ProgressActVo> getInstanceProgressInfo(RequestVo requestVo) throws MyException {
        List<ProgressActVo> list = null;
        try {
            if (null != requestVo.getProcessInstanceId()) {
                String instanceId = requestVo.getProcessInstanceId();
                list = taskInfoMapper.getInstanceProgressInfo(instanceId);
                if (!list.isEmpty()) {
                    list.get(list.size() - 1).setIsNowTask(true);
                    String processDefinitionId = requestVo.getProcessDefinitionId();
                    if (null == processDefinitionId || "".equals(processDefinitionId)) {
                        //查询流程实例所属流程定义ID
                        processDefinitionId = taskInfoMapper.getProcessDefinitionIdByInstanceId(instanceId);
                    }
                    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
                    Process process = bpmnModel.getProcesses().get(0);
                    Collection<UserTask> flowElements1 = process.findFlowElementsOfType(UserTask.class);
                    for (UserTask userTask : flowElements1) {
                        int i = 1;
                        for (ProgressActVo act : list) {
                            if (null != act.getActName() && null != userTask.getName()) {
                                if (act.getActName().equals(userTask.getName())) {
                                    i = 0;
                                    break;
                                }
                            }
                        }
                        if (i == 1) {
                            ProgressActVo progressActVo = new ProgressActVo();
                            progressActVo.setActName(userTask.getName());
                            list.add(progressActVo);
                        }
                    }
                    if (!"endEvent".equals(list.get(list.size() - 1).getActType())) {
                        ProgressActVo progressActVo = new ProgressActVo();
                        progressActVo.setActName("结束");
                        list.add(progressActVo);

                    }

                } else {
                    throw new MyException(CodeEnum.commonException);
                }
            } else {
                throw new MyException(CodeEnum.commonException);
            }
        } catch (Exception e) {
            log.error("生成流程进度信息错误！", e);
            e.printStackTrace();
            throw new MyException(CodeEnum.commonException);
        }
        return list;
    }
}